#include "math.h"
#include <stdlib.h>
#include <iostream>

#include "nw_aligner_tools.cpp"

#define DEGREES 5
#define POINTS_NUM 10

char ALPHABET[] = {'a', 'c', 't', 'g'};
int ALPHABET_SIZE = 4;

class InterCoeffs {
  private:
    double koeffs[DEGREES+1];
  public:
    InterCoeffs() {
        for (int i=0; i<=DEGREES; i++) {
            koeffs[i] = 0;
        };
    };
    InterCoeffs(std::string s) {
        std::vector<std::string> v_strs;
        v_strs = split(s, '|');
        for (int i=0; i<=DEGREES; i++) {
            koeffs[i] = str2double(v_strs[i]);
        };
    };
    InterCoeffs(const InterCoeffs &ic) {
        for (int i=0; i<=DEGREES; i++) {
            koeffs[i] = ic.koeffs[i];
        };
    };

    double calculate(double l) {
        double res = 0;
        for (int i=0; i<=DEGREES; i++) {
            res += koeffs[i] * pow(l, i);
        }
        return res;
    };
};

class InterCoeffsMC {
  private:
    InterCoeffs _ev;
    InterCoeffs _sd;
  public:
    InterCoeffsMC() {};
    InterCoeffsMC(std::string s1, std::string s2) {
        _ev = InterCoeffs(s1);
        _sd = InterCoeffs(s2);
    };
    InterCoeffsMC(std::string s) {
        std::vector<std::string> v_strs = split(s, '\t');
        InterCoeffsMC(v_strs[0], v_strs[1]);
    };
    InterCoeffsMC(const InterCoeffsMC &icm) {
        _ev = InterCoeffs(icm._ev);
        _sd = InterCoeffs(icm._sd);
    };

    double calculate(double length, double x) {
        double ev = _ev.calculate(length);
        double sd = _sd.calculate(length);
        //cout << "MK preview: " << x << "\t" << ev << "\t" << sd << "\t" << length << "\n";
        if (sd > 0)
            return (x - ev) / sd;
        else
            return 0.0;
    };
};

typedef std::map<std::string, InterCoeffsMC> InterpolationData;

int init_interpolation_data(InterpolationData &data, const char* input_fname) {
    std::string line;
    std::vector<std::string> v_strs;
    std::ifstream input_file (input_fname);
    if (not input_file.is_open()) {
        return 1;
    }
    while (input_file.good()) {
        getline(input_file, line);
        if (line.size() == 0) { break; }
        v_strs = split(line, '\t');
        data.insert( std::make_pair(v_strs[0], InterCoeffsMC(v_strs[1], v_strs[2])) );
    };
    return 0;
};

typedef std::map<char, int> CharWeights;

CharWeights create_char_weights(std::string s) {
    CharWeights cw;
    std::map<char, double> mw;
    int length = s.size();
    for (int i=0; i<length; i++) {
        mw[s[i]] += 1.0 / length;
    };
    for(std::map<char, double>::iterator iter = mw.begin(); iter != mw.end(); ++iter) {
        cw[iter->first] = int(iter->second * POINTS_NUM + 0.5);
    }
    return cw;
}

void print_weights(CharWeights &cw) {
    for(std::map<char, int>::iterator iter = cw.begin(); iter != cw.end(); ++iter)
        cout << iter->first << ":\t" << iter->second << "\n";
}

int sum_values(CharWeights &cw) {
    int sum = 0;
    for(std::map<char, int>::iterator iter = cw.begin(); iter != cw.end(); ++iter)
        sum += iter->second;
    return sum;
}

char max_key(CharWeights &cw) {
    int _max = 0;
    char ch;
    for(std::map<char, int>::iterator iter = cw.begin(); iter != cw.end(); ++iter)
        if (iter->second >= _max) {
            _max = iter->second;
            ch = iter->first;
        }
    return ch;
}

char min_key(CharWeights &cw) {
    int _min = 1;
    char ch;
    for(std::map<char, int>::iterator iter = cw.begin(); iter != cw.end(); ++iter)
        if ((iter->second > 0) && (iter->second <= _min)) {
            _min = iter->second;
            ch = iter->first;
        }
    return ch;
}               


void adjust_weights(CharWeights &cw) {
    int diff = sum_values(cw) - POINTS_NUM;
    int ch;
    while (diff != 0) {
        if (diff > 0) 
            cw[max_key(cw)] -= 1;
        else
            cw[min_key(cw)] += 1;
        diff = sum_values(cw) - POINTS_NUM;
    }
}

char complement(char ch) {
    if (ch == 'a')
        return 'g';
    if (ch == 'g')
        return 'a';
    if (ch == 'c')
        return 't';
    if (ch == 't')
        return 'c';
}

std::string create_db_key_pupy(CharWeights &cw1, CharWeights &cw2) {
    char ch;
    bool first = true;
    std::ostringstream s;

    s << POINTS_NUM << "|";

    for(int i=0; i< ALPHABET_SIZE; i++) {
        if (first)
            first = false;
        else
            s << ",";

        ch = ALPHABET[i];
        s << min(cw1[ch], cw2[ch]) << ":" << min(cw1[ch], cw2[complement(ch)]);
    }
    return s.str();
}

